#include "mainwindow.h"
#include "ui_mainwindow.h"

extern  "C"
{
#include <LzmaDec.h>
#include "vFile.h"
#include "Alloc.h"

#include "lzma/inc/LzmaLib.h"
#include "7zFile.h"

#include "bsdiff.h"
#include "bspatch.h"
#include "vFile.h"
}

#define printf(...)
#define BSDIFF_FILE_NAME "pach.bin"

#define BigtoLittle16(x)    ((((uint16_t)(x) & 0xff00) >> 8) | (((uint16_t)(x) & 0x00ff) << 8))
#define BigtoLittle32(x)    ((((uint32_t)(x) & 0xff000000) >> 24) | \
    (((uint32_t)(x) & 0x00ff0000) >> 8) | \
    (((uint32_t)(x) & 0x0000ff00) << 8) | \
    (((uint32_t)(x) & 0x000000ff) << 24))

#define IH_MAGIC   0x56190527   /* Image Magic Number       */
#define IH_NMLEN   (32 - 4)     /* Image Name Length        */
#define IH_NMLEN   (32 - 4)     /* Image Name Length        */
#define BIN_HEAD   "ENDSLEY/BSDIFF43"

typedef struct image_header
{
    uint32_t ih_magic;  /* Image Header Magic Number */
    uint32_t ih_hcrc;   /* Image Header CRC Checksum 差分包包头校验 大端 */
    uint32_t ih_time;   /* Image Creation Timestamp */
    uint32_t ih_size;   /* Image Data Size 差分包的大小 大端 */
    uint32_t ih_load;   /* Data Load Address 上一版本旧文件的大小 */
    uint32_t ih_ep;     /* Entry Point Address 要升级的新文件的大小 */
    uint32_t ih_dcrc;   /* Image Data CRC Checksum 新文件的CRC 大端 */
    uint8_t  ih_os;     /* Operating System */
    uint8_t  ih_arch;   /* CPU architecture */
    uint8_t  ih_type;   /* Image Type */
    uint8_t  ih_comp;   /* Compression Type */
    uint8_t  ih_name[IH_NMLEN]; /* Image Name */
    uint32_t ih_ocrc;   /* Old Image Data CRC Checksum 上一版本旧文件的CRC 大端*/
} image_header_t;


static image_header_t header;

static const unsigned int crc32tab[] =
{
    0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL,
    0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L,
    0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L,
    0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L,
    0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
    0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L,
    0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL,
    0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L,
    0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L,
    0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
    0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L,
    0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L,
    0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L,
    0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL,
    0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
    0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL,
    0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL,
    0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L,
    0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L,
    0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
    0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL,
    0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L,
    0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL,
    0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L,
    0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
    0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL,
    0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L,
    0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L,
    0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L,
    0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
    0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L,
    0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL,
    0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL,
    0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L,
    0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
    0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L,
    0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL,
    0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L,
    0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL,
    0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
    0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L,
    0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL,
    0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L,
    0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L,
    0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
    0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL,
    0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L,
    0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL,
    0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL,
    0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
    0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L,
    0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L,
    0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL,
    0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L,
    0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
    0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L,
    0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L,
    0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL,
    0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L,
    0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
    0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L,
    0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL,
    0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L,
    0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL
};


unsigned int crc32(const unsigned char *buf, unsigned int size)
{
    unsigned int i, crc;
    crc = 0xFFFFFFFF;

    for (i = 0; i < size; i++)
    {
        crc = crc32tab[(crc ^ buf[i]) & 0xff] ^ (crc >> 8);
    }
    return crc ^ 0xFFFFFFFF;
}



MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    headFlag = true;
    
    // 加载用户上次的语言设置
    QSettings settings("Diff_Tool", "Settings");
    QString language = settings.value("Language", "Chinese").toString();
    isChineseLanguage = (language == "Chinese");
    
    // 默认内容格式化
    retranslateUi();
    
    // 如果是英文界面，需要立即应用
    if (!isChineseLanguage) {
        switchLanguage();
    }
}

// 初始化或重置UI文本
void MainWindow::retranslateUi()
{
    // 设置默认文本编辑框内容，保持格式一致
    QString defaultHtml;
    if (isChineseLanguage) {
        defaultHtml = QString(
            "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">"
            "<html><head><meta name=\"qrichtext\" content=\"1\" />"
            "<style type=\"text/css\">p, li { white-space: pre-wrap; }</style></head>"
            "<body style=\" font-family:'SimSun'; font-size:9pt; font-weight:400; font-style:normal;\">"
            "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'SimSun';\"><br /></p>"
            "<p align=\"center\" style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">"
            "<span style=\" font-family:'SimSun'; font-size:16pt; font-weight:600;\">差分升级工具</span></p>"
            "<p align=\"center\" style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">"
            "<span style=\" font-family:'SimSun'; font-size:10pt; font-weight:600;\">(V0.2.0)</span></p>"
            "<p align=\"center\" style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">"
            "<span style=\" font-family:'SimSun'; font-size:10pt; font-weight:600;\">技术支持(vx:18219255930)</span></p>"
            "<p align=\"justify\" style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">"
            "<span style=\" font-family:'SimSun'; font-size:12pt;\">    该工具使用差分算法制作差分包并自带压缩功能，差分升级是将新老固件具有差异的部分剥离出来，例如固件从V1.1.0升级到V1.1.1，两个固件相比只修改了1K的内容，将该部分剥离出来生成差分包pach.bin(v1.1.0~v1.11)，通过OTA将差分包升级到设备端，设备端接收完成之后，先解压差分包，再通过差分恢复算法，根据差分包中的数据标志，将新老固件进行融合，变成新的固件，从而完成升级。</span></p>"
            "<p align=\"justify\" style=\"-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'SimSun'; font-size:12pt;\"><br /></p></body></html>"
        );
    } else {
        defaultHtml = QString(
            "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">"
            "<html><head><meta name=\"qrichtext\" content=\"1\" />"
            "<style type=\"text/css\">p, li { white-space: pre-wrap; }</style></head>"
            "<body style=\" font-family:'SimSun'; font-size:9pt; font-weight:400; font-style:normal;\">"
            "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'SimSun';\"><br /></p>"
            "<p align=\"center\" style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">"
            "<span style=\" font-family:'SimSun'; font-size:16pt; font-weight:600;\">Differential upgrade tool</span></p>"
            "<p align=\"center\" style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">"
            "<span style=\" font-family:'SimSun'; font-size:10pt; font-weight:600;\">(V0.2.0)</span></p>"
            "<p align=\"center\" style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">"
            "<span style=\" font-family:'SimSun'; font-size:10pt; font-weight:600;\">Technical support(vx:18219255930)</span></p>"
            "<p align=\"justify\" style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">"
            "<span style=\" font-family:'SimSun'; font-size:12pt;\">    This tool uses a differential algorithm to make differential packages with built-in compression. Differential upgrade extracts the different parts between old and new firmware. For example, upgrading from V1.1.0 to V1.1.1, if only 1K content is modified, this part is extracted to generate a differential package pach.bin(v1.1.0~v1.11). The device receives and decompresses the package, then merges the old and new firmware according to the data flags in the differential package to complete the upgrade.</span></p>"
            "<p align=\"justify\" style=\"-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'SimSun'; font-size:12pt;\"><br /></p></body></html>"
        );
    }
    
    // 仅当文本编辑框为空时才设置默认内容
    if (ui->textEdit_ReceiveMsg->toPlainText().isEmpty()) {
        ui->textEdit_ReceiveMsg->setHtml(defaultHtml);
    }
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_pushButton_clicked()
{
    //从输入获取文件
    QString printTemp;
    QString initialName;
    QString tempStr;
    QString fileName = QFileDialog::getOpenFileName(this, tr("choose file"), initialName);

    fileName = QDir::toNativeSeparators(fileName);  //转换文件斜杠的方向
    printTemp = tr2("当前导入文件1：", "Current import file1: ") + fileName;
    ui->textEdit_ReceiveMsg->append(printTemp);
    QFile file(fileName);  //文件对象

    /* 打开文件 */
    if(!file.open(QIODevice::ReadOnly | QIODevice::Unbuffered))
    {
        ui->textEdit_ReceiveMsg->append("\n" + tr2("文件1打开失败!", "File1 open failed!") + "\n");
        QMessageBox::information(this,"error:", tr2("文件打开失败！\n请选择bin文件。", "File open failed!\nPlease select bin file."));
        return;
    }

    /* 判断输入的是不是bin文件 */
    QFileInfo binFileInfo = QFileInfo(fileName);
    if (binFileInfo.suffix() != "bin")
    {
        ui->textEdit_ReceiveMsg->append("\n" + tr2("文件1打开失败，请选择bin文件!", "File1 open failed, please select bin file!") + "\n");
        QMessageBox::information(this, "error:", tr2("文件打开失败!\n请选择bin文件!", "File open failed!\nPlease select bin file!"));
        return;
    }
    ui->label_file1->setText(tr2("旧版本软件: ", "Old version software: ") + binFileInfo.fileName());

    QDataStream BinFileData(&file);
    char *binBuff = new char[file.size()];  //要读取bin文件的大小
    BinFileData.readRawData(binBuff, static_cast<int>(file.size()));
    oldFile = QByteArray(binBuff,static_cast<int>(file.size())); //将bin内容写入buff
}


void MainWindow::on_pushButton_2_clicked()
{
    //从输入获取文件
    QString printTemp;
    QString initialName;
    QString tempStr;
    QString fileName = QFileDialog::getOpenFileName(this, tr("choose file"), initialName);

    fileName = QDir::toNativeSeparators(fileName);  //转换文件斜杠的方向
    printTemp = tr2("当前导入文件2：", "Current import file2: ") + fileName;
    ui->textEdit_ReceiveMsg->append(printTemp);
    QFile file(fileName);  //文件对象

    /* 打开文件 */
    if(!file.open(QIODevice::ReadOnly | QIODevice::Unbuffered))
    {
        ui->textEdit_ReceiveMsg->append("\n" + tr2("文件2打开失败!", "File2 open failed!") + "\n");
        QMessageBox::information(this,"error:", tr2("文件打开失败！\n请选择bin文件。", "File open failed!\nPlease select bin file."));
        return;
    }

    /* 判断输入的是不是bin文件 */
    QFileInfo binFileInfo = QFileInfo(fileName);
    if (binFileInfo.suffix() != "bin")
    {
        ui->textEdit_ReceiveMsg->append("\n" + tr2("文件2打开失败，请选择bin文件!", "File2 open failed, please select bin file!") + "\n");
        QMessageBox::information(this, "error:", tr2("文件打开失败!\n请选择bin文件!", "File open failed!\nPlease select bin file!"));
        return;
    }
    ui->label_file2->setText(tr2("新版本软件: ", "New version software: ") + binFileInfo.fileName());

    QDataStream BinFileData(&file);
    char *binBuff = new char[file.size()];  //要读取bin文件的大小
    BinFileData.readRawData(binBuff, static_cast<int>(file.size()));
    newFile = QByteArray(binBuff,static_cast<int>(file.size())); //将bin内容写入buff
}


int bsdiff_write(struct bsdiff_stream *stream, const void *buffer, int size)
{
    FILE *pf = (FILE *) stream->opaque;
    fwrite((void *)buffer, 1, size, pf);
    fseek(pf, 0, SEEK_END);
    return 0;
}


void *cppMyAlloc(size_t size)
{
    char* p =  new char[size];
    return (void*)p;
}

void cppMyFree(void *address)
{
    free(address);
}

long fsize(FILE *fp)
{
    long n;
    fpos_t fpos;
    fgetpos(fp, &fpos);
    fseek(fp, 0, SEEK_END);
    n = ftell(fp);
    fsetpos(fp, &fpos);
    return n;
}


int MainWindow::lzmaProcess(int numArgs, const char *args[], char *rs)
{
    char c;
    int res;
    int encodeMode;
    unsigned long long Dsize;
    unsigned long long Lsize;
    FILE *fp;
    (void)rs;
    (void)numArgs;

    if (numArgs < 3 || numArgs > 4 || strlen(args[1]) != 1)
    {
        ui->textEdit_ReceiveMsg->append(tr2("lzmaProcess参数错误, numArgs = ", "lzmaProcess numArgs err, numArgs = ") + QString::number(numArgs));
        return 1;
    }

    c = args[1][0];
    encodeMode = (c == 'e' || c == 'E');
    if (!encodeMode && c != 'd' && c != 'D')
    {
        ui->textEdit_ReceiveMsg->append(tr2("lzmaProcess编码模式错误.", "lzmaProcess encodeMode err."));
        return 1;
    }

    if (!encodeMode)  /* 压缩文件 */
    {
        fp = fopen(args[2], "rb+");  /* 打开待压缩的文件 */
        if (fp == NULL)
            return 1;

        Dsize = fsize(fp);  /* 获取待压缩的文件大小 */
        ui->textEdit_ReceiveMsg->append(tr2("开始压缩差分包,压缩大小：", "Start compressing diff package, compression size: ") + QString::number(Dsize));
        unsigned char *Dfile = new unsigned char[Dsize];  /* 创建待压缩的文件内存 */

        fread(Dfile, 1, Dsize, fp);  /* 将待压缩的文件写入内存 */

        unsigned char *Lfile = new unsigned char[Dsize];  /* 申请压缩内存 */

        Lsize = Dsize;
        res = LzmaCompress(Lfile, &Lsize, Dfile, Dsize);  /* 压缩到内存 */
        if (res != SZ_OK)
        {
            ui->textEdit_ReceiveMsg->append(tr2("lzma压缩失败, 结果:", "lzma failed to compress, res:") + QString::number(res));
            return res;
        }
        free(Dfile);
        ui->textEdit_ReceiveMsg->append(tr2("压缩成功,压缩后大小:", "Compression successful, compressed size:") + QString::number(Lsize));
        fclose(fp);

        fp = fopen(args[3], "wb+");  /* 创建一个空文件 */
        if (fp == NULL)
            return 1;

        fwrite(Lfile, 1, Lsize, fp);  /* 写入压缩文件 */
        fclose(fp);
        free(Lfile);
    }
    else  /* 解压文件 */
    {
        fp = fopen(args[2], "rb+");
        if (fp == NULL)
            return 1;

        long Esize = fsize(fp);  /* 获取待解压的文件大小 */
        ui->textEdit_ReceiveMsg->append(tr2("包大小:", "Pack size:") + QString::number(Esize));

        uint8_t *Efile = new uint8_t[Esize];  /* 创建待解压的文件内存 */
        fread(Efile, 1, Esize, fp);  /* 将待解压的文件写入内存 */

        Byte Probs[LZMA_PROPS_SIZE + 8];  /* 放入文件头 */

        memcpy(Probs, Efile, sizeof(Probs)); /* 获取文件头 */
        Esize -= sizeof(Probs);

        UInt64 unpack_size = 0;
        for (int i = 0; i < 8; i++)
        {
            unpack_size |= (unsigned char)Probs[LZMA_PROPS_SIZE + i] << (i * 8);
        }

        CLzmaDec state;
        size_t inPos = 0, inSize = 0, outPos = 0;
        //        ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
        //        ELzmaStatus status;

        Byte inBuf[1024];
        Byte outBuf[1024];

        LzmaDec_Construct(&state);
        LzmaDec_Allocate(&state, Probs, LZMA_PROPS_SIZE, &g_Alloc);
        LzmaDec_Init(&state);
        vFile *vpf = vfopen(Efile + sizeof(Probs), Esize);

        fp = fopen(args[3], "wb+");
        if (fp == NULL)
            return 1;

        for (;;)
        {
            // printf("inPos:%lu ", inPos);
            if (inPos == inSize)
            {
                inSize = sizeof(inBuf);
                inSize = vfread(vpf, inBuf, inSize);
                inPos = 0;
                // printf("inSize:%lu ", inSize);
            }
            {
                SizeT inProcessed = inSize - inPos;
                SizeT outProcessed = sizeof(outBuf) - outPos;
                ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
                ELzmaStatus status;

                if (outProcessed > unpack_size)
                {
                    outProcessed = (SizeT)unpack_size;
                    finishMode = LZMA_FINISH_END;
                }

                res = LzmaDec_DecodeToBuf(&state, outBuf + outPos, &outProcessed,
                                          inBuf + inPos, &inProcessed, finishMode, &status);
                inPos += inProcessed;
                outPos += outProcessed;
                unpack_size -= outProcessed;

                if (outPos > 0)
                {
                    // printf("outPos:%lu\n", outPos);
                    fwrite(outBuf, 1, outPos, fp);  /* 写入结果文件 */
                    fseek(fp, 0, SEEK_END);
                }
                outPos = 0;

                if (res != SZ_OK)
                {
                    printf("Unpack failed, exit:%d\n", res);
                    break;
                }
                if (inProcessed == 0 && outProcessed == 0)
                {

                    if (status != LZMA_STATUS_FINISHED_WITH_MARK)
                    {
                        printf("Unpack success, new file size: %lu\n", ftell(fp));
                        res = SZ_ERROR_DATA;
                    }
                    break;
                }
            }
        }
        free(Efile);
        fclose(fp);
    }

    return res;
}



void MainWindow::on_pushButton_3_clicked()
{
    QString newName = BSDIFF_FILE_NAME;
    QFile newFileQFile(newName);  //文件对象

    if(oldFile.size() == 0 || newFile.size() == 0)
    {
        ui->textEdit_ReceiveMsg->append(tr2("请先导入文件!", "Please import files first!"));
        return;
    }

    if (newFileQFile.exists())  //如果文件已存在，则删除该文件
    {
        remove(BSDIFF_FILE_NAME);
    }

    if (!newFileQFile.exists())  //如果文件不存在，则打开关闭一次自动创建
    {
        ui->textEdit_ReceiveMsg->append("\n" + tr2("创建文件:", "Creating file:") + newName);
        newFileQFile.open(QIODevice::WriteOnly);
        newFileQFile.close();
    }

    if(!newFileQFile.open(QIODevice::WriteOnly))
    {
        ui->textEdit_ReceiveMsg->append("\n" + tr2("新文件打开失败!", "New file open failed!") + "\n");
        return;
    }
    ui->textEdit_ReceiveMsg->append(tr2("开始制作差分包", "Start creating diff package"));

    FILE *fp = fdopen(newFileQFile.handle(), "wb+"); //创建二进制文件打开方式

    struct bsdiff_stream bsStream;
    bsStream.opaque = fp;
    bsStream.malloc = cppMyAlloc;
    bsStream.free = cppMyFree;
    bsStream.write = bsdiff_write;

    uint64_t oldSize= oldFile.size();
    uint64_t newSize = newFile.size();

    uint8_t * pOldFile = new uint8_t[oldSize];
    uint8_t * pNewFile = new uint8_t[newSize];
    memcpy(pOldFile, oldFile, oldSize);
    memcpy(pNewFile, newFile, newSize);
    /* 组织文件头 */
    header.ih_load = oldSize;  /* 上一版本旧文件的大小 */
    header.ih_ocrc = BigtoLittle32(crc32(pOldFile, (unsigned int)oldSize));   /* 原文件CRC大端放入 */
    header.ih_ep = newSize;  /* 要升级的新文件的大小 */
    header.ih_dcrc = BigtoLittle32(crc32(pNewFile, (unsigned int)newSize));  /* 新文件CRC大端放入 */

    int res = bsdiff(pOldFile, oldSize, pNewFile, newSize, &bsStream);
    free(pOldFile);
    free(pNewFile);
    if (res)
    {
        ui->textEdit_ReceiveMsg->append(tr2("差分失败", "Differential failure"));
        return;
    }
    long pachSize = fsize(fp);
    ui->textEdit_ReceiveMsg->append(tr2("差分成功,差分包大小:", "Differential success, package size:") + QString::number(pachSize));
    fclose(fp);
    /* 差分后需要压缩 */
    unsigned char bin_head[16 + 8];  /* strlen(BIN_HEAD)+要升级的文件长度 */
    const char *lzargs[4] = {NULL};
    lzargs[0] = NULL;
    lzargs[1] = "d";
    QByteArray tempQb = newName.toLatin1();  //文件名
    lzargs[2] = tempQb.data();
    lzargs[3] = "bstemp.bin";
    char rs;
    res = lzmaProcess(sizeof(lzargs) / sizeof(char *), lzargs, &rs);
    if (res != 0)
    {
        ui->textEdit_ReceiveMsg->append(tr2("压缩差分包失败！", "Compressing diff package failed!"));
        return;
    }

    FILE *newFp = fopen("bstemp.bin", "rb+");  /* 打开待插入的差分文件 */
    if (newFp == NULL)
    {
        ui->textEdit_ReceiveMsg->append(tr2("不能打开新文件.", "Cannot open new file."));
        return;
    }
    pachSize = fsize(newFp);
    ui->textEdit_ReceiveMsg->append(tr2("差分完成, pack包大小:", "Differential complete, pack size:") + QString::number(pachSize));

    /* 在压缩好的差分文件上添加文件头 */
    long headSize = sizeof(image_header_t) + sizeof(bin_head);
    char *newPachFile = new char[(pachSize + headSize)]; /* 创建待插入的文件内存 */
    /* 组织文件头 */
    header.ih_magic = IH_MAGIC;
    header.ih_size = BigtoLittle32(pachSize + sizeof(bin_head));
    header.ih_os = 0;
    header.ih_arch = 0;
    header.ih_type = 0;
    header.ih_comp = 0;
    header.ih_hcrc = 0;
    memset(header.ih_name, 0, IH_NMLEN);
    header.ih_hcrc = BigtoLittle32(crc32((uint8_t *)&header, sizeof(image_header_t)));
    /* 插入内存 */
    memcpy(newPachFile, (uint8_t *)&header, sizeof(image_header_t));

    /* 插入bin文件头 */
    memcpy(bin_head, "ENDSLEY/BSDIFF43", 16);
    offtout(header.ih_ep, bin_head + 16);
    memcpy(newPachFile + sizeof(image_header_t), bin_head, sizeof(bin_head));

    /* 处理待差分的新文件写到内存 */
    if(headFlag)
    {
        fread(newPachFile + headSize, 1, pachSize, newFp);
        fseek(newFp, 0, SEEK_SET);
        fwrite((void *)newPachFile, 1, pachSize + headSize, newFp);
    }
    else
    {
        fread(newPachFile, 1, pachSize, newFp);
        fseek(newFp, 0, SEEK_SET);
        fwrite((void *)newPachFile, 1, pachSize, newFp);
    }
    pachSize = fsize(newFp);
    fclose(newFp);
    free(newPachFile);

    remove(lzargs[2]); /* 删除未压缩的文件 */
    rename("bstemp.bin", lzargs[2]); /* 将临时文件重命名为用户指定的文件名 */
    ui->label_file3->setText(tr2("差分升级包: ", "Differential upgrade package: ") + qApp->applicationDirPath() + "/" + newName);

    ui->textEdit_ReceiveMsg->append(tr2("差分包信息：", "Differential package information:"));
    ui->textEdit_ReceiveMsg->append(tr2("旧bin长度:", "old_bin len:") + QString::number(header.ih_load) + ", crc:" + QString().sprintf("0X%04X", BigtoLittle32(header.ih_ocrc)));
    ui->textEdit_ReceiveMsg->append(tr2("新bin长度:", "new_bin len:") + QString::number(header.ih_ep) + ", crc:" + QString().sprintf("0X%04X", BigtoLittle32(header.ih_dcrc)));
    if(headFlag)
    {
        ui->textEdit_ReceiveMsg->append(tr2("补丁大小+头部 = ", "patchsize+header = ") + QString::number(BigtoLittle32(header.ih_size)));
    }
    else
    {
        ui->textEdit_ReceiveMsg->append(tr2("补丁大小 = ", "patchsize = ") + QString::number(pachSize));
    }
}



void MainWindow::on_checkBox_stateChanged(int arg1)
{
    (void)arg1;
    if(ui->checkBox->isChecked())
    {
        headFlag = true;
        ui->textEdit_ReceiveMsg->append(tr2("自动加文件头", "Automatically add file header"));
    }else{
        headFlag = false;
        ui->textEdit_ReceiveMsg->append(tr2("不加文件头", "No file header"));
    }
}

void MainWindow::on_pushButton_4_clicked()
{
    ui->textEdit_ReceiveMsg->clear();
}


//打开差分文件所在目录按钮
void MainWindow::on_pushButton_5_clicked()
{
    QProcess process;
    QFile newFileQFile(BSDIFF_FILE_NAME);  //文件对象
    QFileInfo binFileInfo = QFileInfo(newFileQFile);

    if (!newFileQFile.exists())  //如果文件不存在，则返回
    {
        ui->textEdit_ReceiveMsg->append(tr2("差分文件不存在！", "Differential file does not exist!"));
        return;
    }

    QString filePath = binFileInfo.fileName();

    filePath.replace("/", "\\"); // 只能识别 "\"
    QString cmd = QString("explorer.exe /select,\"%1\"").arg(filePath);
    qDebug() << cmd;
    process.startDetached(cmd);
}

void MainWindow::on_textEdit_ReceiveMsg_textChanged()
{
    ui->textEdit_ReceiveMsg->moveCursor(QTextCursor::End);
}


void MainWindow::on_switchLanguageButton_clicked()
{
    // 切换语言
    isChineseLanguage = !isChineseLanguage;
    switchLanguage();
}

void MainWindow::switchLanguage()
{
    if (isChineseLanguage) {
        // 切换到中文
        qApp->removeTranslator(&translator);
        ui->switchLanguageButton->setText("English");
        
        // 更新UI文本
        this->setWindowTitle("差分文件工具");
        ui->pushButton->setText("导入旧版本");
        ui->pushButton_2->setText("导入新版本");
        ui->pushButton_3->setText("差分文件");
        ui->pushButton_4->setText("清屏");
        ui->pushButton_5->setText("打开差分文件所在目录");
        ui->checkBox->setText("自动添加文件头");
        
        // 更新标签文本
        if (ui->label_file1->text().contains("Old version software")) {
            ui->label_file1->setText(" 请导入旧版本软件");
        } else {
            QString fileName = ui->label_file1->text();
            if (fileName.contains("Old version software:")) {
                fileName.replace("Old version software:", "旧版本软件:");
                ui->label_file1->setText(fileName);
            }
        }
        
        if (ui->label_file2->text().contains("New version software")) {
            ui->label_file2->setText(" 请导入新版本软件");
        } else {
            QString fileName = ui->label_file2->text();
            if (fileName.contains("New version software:")) {
                fileName.replace("New version software:", "新版本软件:");
                ui->label_file2->setText(fileName);
            }
        }
        
        if (ui->label_file3->text().contains("Click to generate diff package")) {
            ui->label_file3->setText(" 导入后点击生成差分包:pach.bin");
        } else {
            QString fileName = ui->label_file3->text();
            if (fileName.contains("Differential upgrade package:")) {
                fileName.replace("Differential upgrade package:", "差分升级包:");
                ui->label_file3->setText(fileName);
            }
        }
        
        // 更新文本编辑框内容 - 保持HTML格式
        QString currentText = ui->textEdit_ReceiveMsg->toHtml();
        if(currentText.contains("Differential upgrade tool")) {
            // 替换标题
            currentText.replace("Differential upgrade tool", "差分升级工具");
            // 替换技术支持
            currentText.replace("Technical support", "技术支持");
            // 替换说明文本
            if(currentText.contains("This tool uses a differential algorithm")) {
                int startPos = currentText.indexOf("This tool uses a differential algorithm");
                int endPos = currentText.indexOf("</p>", startPos);
                QString enDescription = currentText.mid(startPos, endPos - startPos);
                
                QString cnDescription = "该工具使用差分算法制作差分包并自带压缩功能，差分升级是将新老固件具有差异的部分剥离出来，例如固件从V1.1.0升级到V1.1.1，两个固件相比只修改了1K的内容，将该部分剥离出来生成差分包pach.bin(v1.1.0~v1.11)，通过OTA将差分包升级到设备端，设备端接收完成之后，先解压差分包，再通过差分恢复算法，根据差分包中的数据标志，将新老固件进行融合，变成新的固件，从而完成升级。";
                
                currentText.replace(enDescription, cnDescription);
            }
            
            // 翻译当前日志内容，保持HTML格式
            currentText.replace("Current import file", "当前导入文件");
            currentText.replace("Start making differential package", "开始制作差分包");
            currentText.replace("File open failed", "文件打开失败");
            currentText.replace("Please select bin file", "请选择bin文件");
            currentText.replace("Please import files first", "请先导入文件");
            currentText.replace("Create file", "创建文件");
            currentText.replace("Compress successfully", "压缩成功");
            currentText.replace("start compressing", "开始压缩");
            currentText.replace("compression size", "压缩大小");
            currentText.replace("compression size after", "压缩后大小");
            currentText.replace("Differential failure", "差分失败");
            currentText.replace("Differential success", "差分成功");
            currentText.replace("differential package size", "差分包大小");
            currentText.replace("Compressing the diff package failed", "压缩差分包失败");
            currentText.replace("Differential package information", "差分包信息");
            currentText.replace("Cannot open new file", "不能打开新文件");
            currentText.replace("Differential file does not exist", "差分文件不存在");
            currentText.replace("Automatically add file header", "自动加文件头");
            currentText.replace("No file header", "不加文件头");
            
            ui->textEdit_ReceiveMsg->setHtml(currentText);
        }
    } else {
        // 切换到英文
        ui->switchLanguageButton->setText("中文界面");
        
        // 更新UI文本
        this->setWindowTitle("Differential File Tool");
        ui->pushButton->setText("Import Old Ver");
        ui->pushButton_2->setText("Import New Ver");
        ui->pushButton_3->setText("Generate Diff");
        ui->pushButton_4->setText("Clear");
        ui->pushButton_5->setText("Open Diff Directory");
        ui->checkBox->setText("Auto add file header");
        
        // 更新标签文本
        if (ui->label_file1->text().contains("请导入旧版本软件")) {
            ui->label_file1->setText(" Old version software");
        } else {
            QString fileName = ui->label_file1->text();
            if (fileName.contains("旧版本软件:")) {
                fileName.replace("旧版本软件:", "Old version software:");
                ui->label_file1->setText(fileName);
            }
        }
        
        if (ui->label_file2->text().contains("请导入新版本软件")) {
            ui->label_file2->setText(" New version software");
        } else {
            QString fileName = ui->label_file2->text();
            if (fileName.contains("新版本软件:")) {
                fileName.replace("新版本软件:", "New version software:");
                ui->label_file2->setText(fileName);
            }
        }
        
        if (ui->label_file3->text().contains("导入后点击生成差分包")) {
            ui->label_file3->setText(" Click to generate diff package:pach.bin");
        } else {
            QString fileName = ui->label_file3->text();
            if (fileName.contains("差分升级包:")) {
                fileName.replace("差分升级包:", "Differential upgrade package:");
                ui->label_file3->setText(fileName);
            }
        }
        
        // 更新文本编辑框内容 - 保持HTML格式
        QString currentText = ui->textEdit_ReceiveMsg->toHtml();
        if(currentText.contains("差分升级工具")) {
            // 替换标题
            currentText.replace("差分升级工具", "Differential upgrade tool");
            // 替换技术支持
            currentText.replace("技术支持", "Technical support");
            // 替换说明文本
            if(currentText.contains("该工具使用差分算法")) {
                int startPos = currentText.indexOf("该工具使用差分算法");
                int endPos = currentText.indexOf("</p>", startPos);
                QString cnDescription = currentText.mid(startPos, endPos - startPos);
                
                QString enDescription = "This tool uses a differential algorithm to make differential packages with built-in compression. Differential upgrade extracts the different parts between old and new firmware. For example, upgrading from V1.1.0 to V1.1.1, if only 1K content is modified, this part is extracted to generate a differential package pach.bin(v1.1.0~v1.11). The device receives and decompresses the package, then merges the old and new firmware according to the data flags in the differential package to complete the upgrade.";
                
                currentText.replace(cnDescription, enDescription);
            }
            
            // 翻译当前日志内容，保持HTML格式
            currentText.replace("当前导入文件", "Current import file");
            currentText.replace("开始制作差分包", "Start making differential package");
            currentText.replace("文件打开失败", "File open failed");
            currentText.replace("请选择bin文件", "Please select bin file");
            currentText.replace("请先导入文件", "Please import files first");
            currentText.replace("创建文件", "Create file");
            currentText.replace("压缩成功", "Compress successfully");
            currentText.replace("开始压缩", "start compressing");
            currentText.replace("压缩大小", "compression size");
            currentText.replace("压缩后大小", "compression size after");
            currentText.replace("差分失败", "Differential failure");
            currentText.replace("差分成功", "Differential success");
            currentText.replace("差分包大小", "differential package size");
            currentText.replace("压缩差分包失败", "Compressing the diff package failed");
            currentText.replace("差分包信息", "Differential package information");
            currentText.replace("不能打开新文件", "Cannot open new file");
            currentText.replace("差分文件不存在", "Differential file does not exist");
            currentText.replace("自动加文件头", "Automatically add file header");
            currentText.replace("不加文件头", "No file header");
            
            ui->textEdit_ReceiveMsg->setHtml(currentText);
        }
    }
    
    // 保存用户的语言选择
    QSettings settings("Diff_Tool", "Settings");
    settings.setValue("Language", isChineseLanguage ? "Chinese" : "English");
}

// 添加tr2函数实现
QString MainWindow::tr2(const QString& textCN, const QString& textEN)
{
    return isChineseLanguage ? textCN : textEN;
}
